package bit.chapter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 *
 * 保存数据时候和数据库二进制函数结合使用
 *
 *merge into SUB_T_CHAPTER_PERM_SUB
 * 		using DUAL
 * 		on (USERID = #userId# and BOOKID = #bookId# and STARTCHAPTER = #startChapter#)
 * 		when matched then
 * 		  update
 * 		     set CHAPTERLIST = UTL_RAW.BIT_OR(CHAPTERLIST, #chapterList#),
 * 		         SUBTIME     = #subTime#
 * 		   where UTL_RAW.BIT_AND(UTL_RAW.BIT_OR(CHAPTERLIST,
 * 		                                        hextoraw('00000000000000000000000000000000000000000000000000')),
 * 		                         UTL_RAW.BIT_OR(#chapterList#,
 * 		                                        hextoraw('00000000000000000000000000000000000000000000000000'))) =
 * 		         hextoraw('00000000000000000000000000000000000000000000000000')
 * 		when not matched then
 * 		  insert
 * 		    (USERID, BOOKID, STARTCHAPTER, CHAPTERLIST, PRODUCTID, MCPID, SPEC, SUBSCRIBEWAY, SUBTIME)
 * 		  values
 * 		    (#userId#, #bookId#, #startChapter#, #chapterList#, #productId#, #mcpId#, #spec#, #subscribeWay#, #subTime#)
 */
public class ChapterNoUtil {
    /**
     * 从缓存中批量查询chapterId映射到chapterNo的关系
     */
    public List<String> queryChapterNosFromCache(String bookId, List<String> chapterIds,
                                                  Map<Integer, List<ChapterNo>> chapterNoMap) {
        List<String> notFundChapterIds = new ArrayList<String>(10);

        List<Integer> nos = null;

        //若缓存异常或者查出来的章节序号为空,则所有章节序号都要到数据库查询
        if (nos.size() == 0) {
            notFundChapterIds.addAll(chapterIds);
            return notFundChapterIds;
        }

        int size = chapterIds.size();
        for (int i = 0; i < size; i++) {
            Integer no = nos.get(i);
            String chapterId = chapterIds.get(i);

            if (no == null) {
                notFundChapterIds.add(chapterId);
            } else {
                putChapterNoToMap(chapterNoMap, new ChapterNo(no, chapterId));
            }
        }

        return notFundChapterIds;
    }

    public void putChapterNoToMap(Map<Integer, List<ChapterNo>> chapterNoMap, ChapterNo chapterNo) {
        List<ChapterNo> chapterNoList = chapterNoMap.get(chapterNo.getStart());
        if (chapterNoList == null) {
            chapterNoList = new ArrayList<ChapterNo>(10);
            //按照每200章汇总 chapterNo.getStart() = 0 chapterNo.getStart() = 200  chapterNo.getStart() = 400
            chapterNoMap.put(chapterNo.getStart(), chapterNoList);
        }
        chapterNoList.add(chapterNo);
    }

    /**
     * 将压缩章节订购记录解析成章节序号
     */
    public static List<ChapterNo> parseChapterNo(int startChapterNo, byte[] chapterList) {
        List<ChapterNo> chapterNoList = new ArrayList<ChapterNo>(50);
        int len = chapterList.length;
        for (int i = 0; i < len; i++) { //循环每一个槽位
            int mask = 0x80;
            for (int j = 0; j < 8; j++) {
                if ((chapterList[i] & mask) == mask) {
                    chapterNoList.add(new ChapterNo(startChapterNo, (i << 3) + j));
                }
                //指向当前槽位的下一个位置
                mask = mask >> 1;
            }
        }
        return chapterNoList;
    }

    /**
     * 得到章节信息
     *
     * @param startChapterNo
     * @param chapterList
     * @return
     */
    public static List<ChapterNo> getChapterNo(int startChapterNo, byte[] chapterList) {
        return parseChapterNo(startChapterNo, chapterList);
    }


    /**
     * 查找待鉴权章节中已订购章节列表
     *
     * @param chapterList   压缩的章节订购记录
     * @param chapterNoList 待鉴权章节序号列表
     * @return 待鉴权章节中已订购章节序号列表
     */
    public static List<ChapterNo> findSubChapterNo(byte[] chapterList, List<ChapterNo> chapterNoList) {
        List<ChapterNo> subChapterNos = new ArrayList<ChapterNo>(20);
        for (ChapterNo chapterNo : chapterNoList) {
            int index = chapterNo.getOffset() >> 3;  //获取章节在第几个槽位
            if (index >= chapterList.length) {
                continue;
            }

            int mask = 0x80 >> (chapterNo.getOffset() & 7); //计算章节所在槽位的位置
            if ((chapterList[index] & mask) == mask) { //chapterList[index] 代表的二进制数，在mask位是否是1 , 如果1代表已经订购过
                subChapterNos.add(chapterNo);
            }
        }

        return subChapterNos;
    }
    /**
     * 生成压缩章节订购字符串
     *
     * @param chapterNoList 章节序号列表
     * @return 压缩后章节订购字符串
     */
    public static byte[] buildChapterList(List<ChapterNo> chapterNoList)
    {
        byte[] chapterList = new byte[25]; //byte 取值范围是 11111111~01111111   每个槽位代表8个章节 共 25*8=200章
        //将数组填充 0
        Arrays.fill(chapterList, (byte) 0);
        for (ChapterNo chapterNo : chapterNoList)
        {
            //例如 chapterNo.getOffset() = 5    二进制  101
            int index = chapterNo.getOffset() >> 3; // chapterNo.getOffset()/8 = 0  判断章节在第几个槽位
            int mask = 0x80 >> (chapterNo.getOffset() & 7); //  101 & 111  = 101(截取后三位等同于除8后取余数)   0x80 >> 101 = 100(计算章节所在槽位中的位置)
            chapterList[index] = (byte) (chapterList[index] | mask);  //0 | 100 = 100(和已经存在的章节组合)
        }
        //章节从0开始
        return tripEnd(chapterList);
        //存放oracle中SUB_T_CHAPTER_PERM_SUB表 CHAPTERLIST字段
        //用工具查肉眼看到的是16进制标识了所以是 50 位(1个16进制 = 4 个 bit = 4个二进制数，所以可以标识200个章节)
    }


    private static byte[] tripEnd(byte[] array) {
        int newLength = array.length;
        while (newLength > 1) {
            if (array[newLength - 1] != 0) {
                break;
            }

            newLength--;
        }

        if (newLength == array.length) {
            return array;
        } else {
            return Arrays.copyOf(array, newLength);
        }
    }
}
